package web

import (
	"context"
	"gitee.com/xfrm/middleware/xcontext"
	"gitee.com/xfrm/middleware/xlog"
	"gitee.com/xfrm/starter/boot/bootservice"
	"github.com/gin-gonic/gin/binding"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"net/http"
)

type HandleRequest interface {
	Handle(ctx context.Context, c *bootservice.Context, clientInfo XFRMClientInfo, authInfo XFRMAuthInfo) (interface{}, error)
}

type FactoryHandleRequest func() HandleRequest

func HttpRequestWrapper(fac FactoryHandleRequest) func(c *bootservice.Context) {
	return buildHttpWrapperFunc(fac, false, nil)
}

func HttpRequestMarshalBodyWrapper(fac FactoryHandleRequest) func(c *bootservice.Context) {
	return buildHttpWrapperFunc(fac, true, nil)
}
func HttpRequestPostFormWrapper(fac FactoryHandleRequest) func(c *bootservice.Context) {
	return buildHttpWrapperFunc(fac, true, binding.FormPost)
}

func buildHttpWrapperFunc(fac FactoryHandleRequest, isMarshalBody bool, binding binding.Binding) func(c *bootservice.Context) {
	return func(c *bootservice.Context) {
		reqHanler := fac()
		if isMarshalBody {
			var err error
			if binding != nil {
				err = c.ShouldBindWith(reqHanler, binding)
			} else {
				err = c.ShouldBind(reqHanler)
			}
			if err != nil {
				sts, _ := status.FromError(err)
				c.JSON(http.StatusOK, ApiReturn{
					Status:  sts.Proto(),
					Payload: nil,
				})
				return
			}
		}
		ctx := xcontext.NewValueContext(c.Request.Context())
		clientInfo, err := GetXFRMClientInfoFromRequest(ctx, c.Request)
		if err != nil {
			xlog.Warnf(ctx, "parse clientinfo header error: %v", err)
		}
		clientInfo.Ip = c.ClientIP()
		authInfo, err := GetXFRMAuthInfoFromRequest(ctx, c.Request)
		if err != nil {
			xlog.Warnf(ctx, "parse authInfo header error: %v", err)
		}
		resp, err := reqHanler.Handle(ctx, c, clientInfo, authInfo)
		if c.IsAborted() {
			return
		}
		if c.Writer.Written() {
			return
		}
		if err != nil {
			sts, _ := status.FromError(err)
			c.JSON(http.StatusOK, ApiReturn{
				Status:  sts.Proto(),
				Payload: nil,
			})
			return
		}
		c.JSON(http.StatusOK, ApiReturn{
			Status:  status.New(codes.OK, "").Proto(),
			Payload: resp,
		})
	}
}
